package com.rengwuxian.coursecoroutines._4_flow

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.transformWhile
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.EmptyCoroutineContext

/**
 * transform()系列操作符
 * transform() / transformWhile() / transformLatest()
 *
 * 1.transform() : 更加底层的map()，返回值是Unit，需要手动往下发送数据emit。map()就是用transform来实现的。
 *   越底层的API虽然越难用，但同时它也更自由。可以定制处理和发送数据的规则。
 * 2.transformWhile() : 相当于一个transform() + takeWhile() 的结合。它的大括号有返回值，如果返回true，就继续，一旦遇到false，就直接掐断Flow，到此为止。
 * 3.transformLatest() : 是mapLatest()的底层版本。
 *   像transform()一样工作，但如果在它处理任何一个数据的过程中有新数据到来，那么它就立即中止这个数据的处理，去开始处理新的那条数据。
 * 输出：
 * 1: 1 - hahaha
 * 1: 2 - hahaha
 * 1: 2 - hahaha
 * 1: 3 - hahaha
 * 1: 3 - hahaha
 * 1: 3 - hahaha
 * 1: 4 - hahaha
 * 1: 4 - hahaha
 * 1: 4 - hahaha
 * 1: 4 - hahaha
 * 1: 5 - hahaha
 * 1: 5 - hahaha
 * 1: 5 - hahaha
 * 1: 5 - hahaha
 * 1: 5 - hahaha
 * 2: 1 - hahaha
 * 2: 2 - hahaha
 * 2: 2 - hahaha
 * 2: 3 - hahaha
 * 2: 3 - hahaha
 * 2: 3 - hahaha
 * 3: 1 - start
 * 3: 2 - start
 * 3: 3 - start
 * 3: 3 - end
 */
fun main() = runBlocking<Unit> {
  val scope = CoroutineScope(EmptyCoroutineContext)
  val flow1 = flowOf(1, 2, 3, 4, 5)
  val flow2 = flow {
    delay(100)
    emit(1)
    delay(100)
    emit(2)
    delay(100)
    emit(3)
  }
  scope.launch {
    flow1.transform {
      if (it > 0) {
        repeat(it) { _ ->
          emit("$it - hahaha")
        }
      }
    }.collect { println("1: $it") }
    flow1.map {  }
    flow1.transformWhile {
      if (it > 3) return@transformWhile false
      if (it > 0) {
        repeat(it) { _ ->
          emit("$it - hahaha")
        }
      }
      true
    }.collect { println("2: $it") }
    flow2.transformLatest {
      delay(50)
      emit("$it - start")
      delay(100)
      emit("$it - end")
    }.collect { println("3: $it") }
  }
  delay(10000)
}